package com.neusoft.bizcore.auth.service.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import javax.persistence.EntityNotFoundException;

import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

import com.neusoft.bizcore.auth.common.bean.OrgTreeBean;
import com.neusoft.bizcore.auth.common.bean.UserBean;
import com.neusoft.bizcore.auth.converter.AuthOganizationConverter;
import com.neusoft.bizcore.auth.dto.AuthOganizationDTO;
import com.neusoft.bizcore.auth.model.AuthOganization;
import com.neusoft.bizcore.auth.model.AuthUser;
import com.neusoft.bizcore.auth.repository.AuthOganizationRepository;
import com.neusoft.bizcore.auth.repository.AuthUserRepository;
import com.neusoft.bizcore.auth.service.AuthOganizationService;
import com.neusoft.bizcore.web.constant.ExceptionConstants;
import com.neusoft.bizcore.web.exception.UnitedException;
import com.neusoft.bizcore.web.support.Searchable;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class AuthOganizationServiceImpl implements AuthOganizationService {

    @Autowired
    private AuthOganizationRepository authOganizationRepository;
    @Autowired
    private AuthOganizationConverter authOganizationConverter;
    @Autowired
    private AuthUserRepository authUserRepository;

    @Override
    public List<AuthOganizationDTO> findAll() {
        final List<AuthOganization> models = this.authOganizationRepository.findAll();
        final List<AuthOganizationDTO> dtos = this.authOganizationConverter.toListDTO(models);
        return dtos;
    }

    @Override
    public Page<AuthOganizationDTO> search(final Pageable pageable, final Searchable searchable) {
        final Page<AuthOganization> models = this.authOganizationRepository.search(pageable, searchable);
        return this.authOganizationConverter.toPageDTO(models);
    }

    @Override
    public AuthOganizationDTO details(final Long id) {
        AuthOganization model;
        try {
            model = this.authOganizationRepository.getOne(id);
        } catch (final EntityNotFoundException e) {
            throw new UnitedException(ExceptionConstants.ENTITY_NOT_FOUND, new Object[] {id });
        }

        return this.authOganizationConverter.toDTO(model);
    }

    @Override
    public AuthOganizationDTO details(final String code) {
        AuthOganization model;
        try {
            model = this.authOganizationRepository.findByCode(code);
        } catch (final EntityNotFoundException e) {
            throw new UnitedException(ExceptionConstants.ENTITY_NOT_FOUND, new Object[] {code });
        }

        return this.authOganizationConverter.toDTO(model);
    }

    @Override
    public AuthOganizationDTO create(final AuthOganizationDTO dto) {
        final AuthOganization model = this.authOganizationConverter.toModel(dto);
        this.authOganizationRepository.save(model);
        return this.authOganizationConverter.toDTO(model);
    }

    @Override
    public void delete(final Long id) {
        try {
            final AuthOganization model = this.authOganizationRepository.getOne(id);
            final List<AuthOganization> children = this.authOganizationRepository.findByParent(model);
            children.forEach(c -> {
                c.setParent(null);
                this.authOganizationRepository.save(c);
            });
            this.authOganizationRepository.delete(model);
        } catch (final EntityNotFoundException e) {
            throw new UnitedException(ExceptionConstants.ENTITY_NOT_FOUND, new Object[] {id });
        }
    }

    @Override
    public AuthOganizationDTO update(final Long id, final AuthOganizationDTO dto) {
        AuthOganization model;
        try {
            model = this.authOganizationRepository.getOne(id);
        } catch (final EntityNotFoundException e) {
            throw new UnitedException(ExceptionConstants.ENTITY_NOT_FOUND, id);
        }

        dto.setId(model.getId());
        model = this.authOganizationConverter.toModel(dto);
        this.authOganizationRepository.save(model);
        return this.authOganizationConverter.toDTO(model);
    }

    @Override
    public AuthOganizationDTO bind(final Long id, final List<Long> users) {
        return this.bingUsers(id, users, false);
    }

    @Override
    public AuthOganizationDTO addUsers(final Long id, final List<Long> users) {
        return this.bingUsers(id, users, true);
    }

    @Override
    public AuthOganizationDTO removeUsers(final Long id, final List<Long> users) {
        AuthOganization model;
        try {
            model = this.authOganizationRepository.getOne(id);
        } catch (final EntityNotFoundException e) {
            throw new UnitedException(ExceptionConstants.ENTITY_NOT_FOUND, id);
        }

        final List<AuthUser> willRemovedUsers =
                model.getUsers().stream().filter(u -> users.contains(u.getId())).collect(Collectors.toList());

        model.getUsers().removeAll(willRemovedUsers);
        this.authOganizationRepository.save(model);

        return this.authOganizationConverter.toDTO(model);

    }

    @Override
    public List<OrgTreeBean> currentUserOrganizationTree(final UserBean user) {

        final List<OrgTreeBean> result = new ArrayList<>();
        final List<String> orgs = user.getOrgs();
        final List<String> deleteOrgs = new ArrayList<>();

        if (CollectionUtils.isNotEmpty(orgs)) {
            orgs.stream().forEach(code -> {
                final AuthOganization oganization = this.authOganizationRepository.findByCode(code);
                if ((oganization.getParent() != null) && orgs.contains(oganization.getParent().getCode())) {
                    deleteOrgs.add(code);
                }
            });
        }

        if (CollectionUtils.isEmpty(orgs)) {
            throw new UnitedException("请联系管理员给用户配置组织机构！");
        }

        orgs.stream().filter(s -> !deleteOrgs.contains(s)).map(k -> this.orgTree(k)).forEach(result::add);
        return result;
    }

    private AuthOganizationDTO bingUsers(final Long id, final List<Long> users, final boolean add) {
        AuthOganization model;
        try {
            model = this.authOganizationRepository.getOne(id);
        } catch (final EntityNotFoundException e) {
            throw new UnitedException(ExceptionConstants.ENTITY_NOT_FOUND, id);
        }
        if (!add) {
            model.getUsers().clear();
        }
        users.stream().map(i -> {
            AuthUser authUser = null;
            try {
                if (!model.getUsers().stream().map(AuthUser::getId).collect(Collectors.toList())
                        .contains(i)) {
                    authUser = this.authUserRepository.getOne(i);
                }
            } catch (final EntityNotFoundException e) {
                AuthOganizationServiceImpl.log.warn("user {} is not found.", i);
            }
            return authUser;
        }).filter(u -> null != u).forEach(model.getUsers()::add);

        this.authOganizationRepository.save(model);

        return this.authOganizationConverter.toDTO(model);
    }

    @Override
    public OrgTreeBean orgTree(final String root) {

        final AuthOganization rootModel = this.authOganizationRepository.findByCode(root);

        final OrgTreeBean rootBean = new OrgTreeBean();
        rootBean.setCode(rootModel.getCode());
        rootBean.setTitle(rootModel.getName());

        this.subTree(rootBean);

        return rootBean;
    }

    private void subTree(final OrgTreeBean parentBean) {
        final List<AuthOganization> childrenModels =
                this.authOganizationRepository.findByParent_code(parentBean.getCode());

        final List<OrgTreeBean> children = new ArrayList<>();
        childrenModels.stream().map(o -> {
            final OrgTreeBean t = new OrgTreeBean();
            t.setCode(o.getCode());
            t.setTitle(o.getName());
            return t;
        }).forEach(children::add);

        if (children.size() > 0) {
            parentBean.setChildren(children);

            children.forEach(s -> {
                this.subTree(s);
            });
        }
    }

}
